package com.thouiot.modbus.util;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * @description modbus master 工具类
 * @author wangkai
 * @date 2025/04/10 15:05
 */
public class ModbusMasterUtil {

    private static final Logger log = LoggerFactory.getLogger(ModbusMasterUtil.class);

    /**
     * 写 [01 Coil Status(0x)]写一个 function ID = 5
     * @param slaveId   slaveId
     * @param writeOffset   位置
     * @param writeValue 值
     * @return 是否写入成功
     */
    public static boolean writeCoil(ModbusMaster master, int slaveId, int writeOffset, boolean writeValue)
            throws ModbusTransportException {
        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
        // 发送请求并获取响应对象
        WriteCoilResponse response = (WriteCoilResponse) master.send(request);
        if (response.isException()) {
            log.error("获取响应失败: " + response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }

    /**
     * 写[01 Coil Status(0x)] 写多个 function ID = 15
     * @param slaveId  slaveId
     * @param startOffset 开始位置
     * @param bdata 写入的数据
     * @return 是否写入成功
     */
    public static boolean writeCoils(ModbusMaster master, int slaveId, int startOffset, boolean[] bdata)
            throws ModbusTransportException {
        // 创建请求
        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);
        if (response.isException()) {
            log.error("获取响应失败: " + response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }

    /***
     * 保存寄存器
     * 写[03 Holding Register(4x)] 写一个 function ID = 6
     * @param slaveId
     * @param writeOffset
     * @param writeValue
     */
    public static boolean writeRegister(ModbusMaster master, int slaveId, int writeOffset, int writeValue)
            throws ModbusTransportException {
        // 创建请求对象
        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
        WriteRegisterResponse response = (WriteRegisterResponse) master.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }


    /**
     * 写入[03 Holding Register(4x)]写多个 function ID=16
     * @param slaveId modbus的slaveID
     * @param startOffset 起始位置偏移量值
     * @param sdata 写入的数据
     * @return 返回是否写入成功
     */
    public static boolean writeRegisters(ModbusMaster master, int slaveId, int startOffset, short[] sdata)
            throws ModbusTransportException {
        // 创建请求对象
        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
        // 发送请求并获取响应对象
        ModbusResponse response = master.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }


    /**
     * 写入数字类型的模拟量（如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long）
     * @param slaveId
     * @param offset
     * @param value 写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
     * @param dataType 参考 {@link com.serotonin.modbus4j.code.DataType}
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     */
    public static boolean writeHoldingRegister(ModbusMaster master, int slaveId, int offset, Number value, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        // 类型
        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
        master.setValue(locator, value);
        return true;
    }

    /**
     * 批量写入 Number 类型
     * @param master
     * @param slaveId
     * @param offset
     * @param values
     * @param dataType
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     */
    public static boolean writeHoldingRegisters(ModbusMaster master, int slaveId, int offset, List<Number> values, int dataType)
            throws ModbusTransportException, ErrorResponseException {

        int registerCount = ModbusConvertUtil.getRegisterCount(dataType);
        for (int i = 0; i < values.size(); i++) {
            // 这里 offset + i * registerCount，而不是 offset + i * registerCount * 2，因为modbus4j 内offset 都是寄存器位的偏移量，即以 short为单位的偏移量
            writeHoldingRegister(master, slaveId, offset + i * registerCount, values.get(i), dataType);
        }
        return true;
    }

    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     * @param slaveId slaveId
     * @param start 位置
     * @param len 读取长度    此长度不是 byte 的长度，而是寄存器位的长度，即 short 的长度
     */
    public static byte[] readHoldingRegisters(ModbusMaster master, int slaveId, int start, int len) throws Exception{
        ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len);
        ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return null;
        }else {
            return response.getData();
        }
    }

    /**
     * 读取[04 Input Register类型 4x]模拟量数据
     * @param master
     * @param slaveId
     * @param start
     * @param len
     * @return
     * @throws Exception
     */
    public static byte[] readInputRegisters(ModbusMaster master, int slaveId, int start, int len) throws Exception{
        ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, start, len);
        ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return null;
        }else {
            return response.getData();
        }
    }

    /**
     * BaseLocator 是 modbus4j 库中的一个重要类，用于定位 Modbus 设备中的数据点。它是所有具体定位器类型的基类。
     * 读取[01 Coil Status(0x)] 状态数据
     * @param master
     * @param slaveId
     * @param offset
     */
    public static boolean readCoilStatus(ModbusMaster master, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Boolean> locator = BaseLocator.coilStatus(slaveId, offset);
        return master.getValue(locator);
    }

    public static boolean[] readCoilStatus(ModbusMaster master, int slaveId, int startOffset, int len) throws ModbusTransportException, ErrorResponseException {
        ReadCoilsRequest request = new ReadCoilsRequest(slaveId, startOffset, len);
        ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return null;
        }else {
            boolean[] booleanData = response.getBooleanData();
            boolean[] result = new boolean[len];
            System.arraycopy(booleanData, 0, result, 0, len);
            return result;
        }
    }

    /**
     * 读取[02 Input Status(1x)] 状态数据
     * @param master
     * @param slaveId
     * @param offset
     */
    public static boolean readInputStatus(ModbusMaster master, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Boolean> locator = BaseLocator.inputStatus(slaveId, offset);
        return master.getValue(locator);
    }

    public static boolean[] readInputStatus(ModbusMaster master, int slaveId, int startOffset, int len) throws ModbusTransportException {
        ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, startOffset, len);
        ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) master.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return null;
        }else {
            boolean[] booleanData = response.getBooleanData();
            boolean[] result = new boolean[len];
            System.arraycopy(booleanData, 0, result, 0, len);
            return result;
        }
    }

    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     * @param master
     * @param slaveId
     * @param offset
     * @param dataType 来自com.serotonin.modbus4j.code.DataType
     */
    public static Number readHoldingRegister(ModbusMaster master, int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
        return master.getValue(locator);
    }

    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     * @param slaveId slaveId
     * @param offset 位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     */
    public static Number readInputRegister(ModbusMaster master, int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        return master.getValue(loc);
    }


    /**
     * 批量读取使用方法
     *
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
//    public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {
//        BatchRead<Integer> batch = new BatchRead<Integer>();
//        batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
//        batch.addLocator(1, BaseLocator.inputStatus(1, 0));
//        ModbusMaster master = getMaster();
//        batch.setContiguousRequests(false);
//        BatchResults<Integer> results = master.send(batch);
//        System.out.println(results.getValue(0));
//        System.out.println(results.getValue(1));
//    }
}
